/**
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 *   
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 */
package org.jiopi.ibean.kernel.config;

import org.jiopi.ibean.kernel.NameVersion;
import org.jiopi.ibean.kernel.context.ModuleConsoleImpl;
import org.jiopi.ibean.kernel.context.classloader.IBeanClassLoader;
import org.dom4j.Document;
import org.dom4j.Element;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

/**
 * 
 * JIOPi Configuration File Parser
 * 
 * @since 2010.5.22
 *
 */
public class JIOPiConfig {
	
	private final HashMap<String,List<Module>> modules = new HashMap<String,List<Module>>();
	
	@SuppressWarnings("unchecked")
	public JIOPiConfig(Document config){
		Element configurationElement = config.getRootElement();
		Iterator<Element> moduleElements = configurationElement.elementIterator("module");
		
		
		while(moduleElements.hasNext()){
			Element moduleElement = moduleElements.next();
			String moduleName = moduleElement.attributeValue("name");
			List<Module> moduleList = modules.get(moduleName);
			if(moduleList == null){
				moduleList = new ArrayList<Module>();
				modules.put(moduleName, moduleList);
			}
			String moduleVersion = moduleElement.attributeValue("version");
			Module m = new Module(moduleName,moduleVersion,moduleElement);
			moduleList.add(m);
		}
		for(String moduleName:modules.keySet()){
			List<Module> moduleList = modules.get(moduleName);
			Collections.sort(moduleList);
		}
	}
	
	public Module getModuleConfig(String name,String version){
		return getModuleConfig(new NameVersion(name,version));
	}
	
	public Module getModuleConfig(NameVersion moduleVersion){
		List<Module> moduleList = modules.get(moduleVersion.name);
		Module m = null;
		if(moduleList!=null){
			for(int i=moduleList.size()-1;i>=0;i--){
				Module module = moduleList.get(i);
				if(module.isCompatible(moduleVersion)){
					return module;
				}
			}
		}
		return m;
	}
	
	public static class Module extends NameVersion{
		
		private final HashMap<String,Controlpanel> controlpanels;
		
		@SuppressWarnings("unchecked")
		public Module(String name, String version,Element moduleElement) {
			super(name, version);
			controlpanels = new HashMap<String,Controlpanel>();
			Iterator<Element> controlpanelElements = moduleElement.elementIterator("controlpanel");
			while(controlpanelElements.hasNext()){
				Element controlpanelElement = controlpanelElements.next();
				String cpName = controlpanelElement.attributeValue("name");
				if(cpName!=null){
					String cpID = controlpanelElement.attributeValue("id");
					if(cpID!=null) cpName=cpName+"?"+cpID;
					Controlpanel cp = new Controlpanel(controlpanelElement);
					controlpanels.put(cpName, cp);
				}
			}
		}
		
		public Controlpanel getMatchedControlpanel(IBeanClassLoader moduleClassLoader,String controlpanelClassName,String configurationID){
			//System.out.println("registerClassName "+registerClassName+" configurationID "+configurationID);
			Controlpanel matchedControlpanel = null;
			Class<?> blueprintClass = null;
			try {
				blueprintClass = moduleClassLoader.loadBlueprintClass(controlpanelClassName);
				//System.out.println("find blueprintClass "+blueprintClass.getName());
			} catch (ClassNotFoundException e) {
			}
			if(blueprintClass!=null){
				for(String controlpanelName:controlpanels.keySet()){
					Controlpanel testControlpanel = controlpanels.get(controlpanelName);
					//test registerClassName
					boolean sameControlpanel = false;
					String[] cpInfo = ModuleConsoleImpl.parseRegisterName(controlpanelName);
					//System.out.println(cpInfo[0]);
					if(cpInfo[0].equals(controlpanelClassName) || cpInfo[0].equals(blueprintClass.getName())){
						sameControlpanel = true;
					}else{
						//use class test
						try {
							Class<?> testClass = moduleClassLoader.loadBlueprintClass(testControlpanel.name);
							if(blueprintClass == testClass) sameControlpanel = true;
						} catch (ClassNotFoundException e) {
						}
					}
					//test configuration
					if(sameControlpanel){
						//System.out.println("!!!"+registerClassName+" "+(testControlpanel.id==null) +" "+(configurationID==null));
						if(testControlpanel.id == null && configurationID == null){
							//System.out.println("match1 "+testControlpanel.name+" "+testControlpanel.id);
							return testControlpanel;
						}
						else if(testControlpanel.id!=null){
							if(testControlpanel.id.equals(configurationID)){
								//System.out.println("match2 "+testControlpanel.name+" "+testControlpanel.id);
								return testControlpanel;
							}
							else if(configurationID == null && matchedControlpanel==null){
								matchedControlpanel = testControlpanel;
							}
						}
					}
				}
			}
//			if(matchedControlpanel!=null){
//				System.out.println("match "+matchedControlpanel.name+" "+matchedControlpanel.id);
//			}else{
//				System.out.println("not match");
//			}
			return matchedControlpanel;
		}
		
	}
	
	
	public static class Controlpanel{
		
		public final String name;
		public final String id;
		
		public final String[] constructor;
		public final HashMap<String,String> properties = new HashMap<String,String>();
		
		/**
		 * 格式 [id:[key:value]]
		 */
		public final HashMap<String,HashMap<String,String>> innerAccessories = new HashMap<String,HashMap<String,String>>();
		public final HashMap<String,HashMap<String,String>> accessories =new HashMap<String,HashMap<String,String>>();
		
		/**
		 * 格式 to from,args[]
		 */
		public final HashMap<String,ArrayList<Object[]>> sockets = new HashMap<String,ArrayList<Object[]>>();
		
		@SuppressWarnings("unchecked")
		public Controlpanel(Element controlpanelElement){
			name = controlpanelElement.attributeValue("name");
			id = controlpanelElement.attributeValue("id");
			
			//parse constructor
			{
				ArrayList<String> constructorList = new ArrayList<String>();
				Element constructorElement =  controlpanelElement.element("constructor");
				if(constructorElement!=null){
					Iterator<Element> args = constructorElement.elementIterator("arg");
					while(args.hasNext()){
						Element argElement = args.next();
						constructorList.add(getPropertyValue(argElement,""));
					}
				}
				constructor = constructorList.toArray(new String[constructorList.size()]);
			}
			
			//parse properties
			{
				Element propertiesElement = controlpanelElement.element("properties");
				if(propertiesElement!=null){
					Iterator<Element> properties = propertiesElement.elementIterator("property");
					while(properties.hasNext()){
						Element property = properties.next();
						String propertyName = property.attributeValue("name");
						String propertyValue = getPropertyValue(property,null);
						this.properties.put(propertyName, propertyValue);
					}
				}
			}
			
			//parse innerAccessories
			{
				Iterator<Element> innerAccessories = controlpanelElement.elementIterator("inner-accessory");
				while(innerAccessories.hasNext()){
					Element innerAccessorie = innerAccessories.next();
					String id = innerAccessorie.attributeValue("id");
					String module = innerAccessorie.attributeValue("module");
					if(id!=null||module!=null){
						String version = innerAccessorie.attributeValue("version");
						String configuration = innerAccessorie.attributeValue("configuration");
						HashMap<String,String> innerAccessorieConfig = new HashMap<String,String>(3);
						innerAccessorieConfig.put("module", module);
						if(version!=null) innerAccessorieConfig.put("version", version);
						if(configuration!=null) innerAccessorieConfig.put("configuration", configuration);
						this.innerAccessories.put(id, innerAccessorieConfig);
					}
				}
			}
			
			//parse Accessories
			{
				Iterator<Element> accessories = controlpanelElement.elementIterator("accessory");
				while(accessories.hasNext()){
					Element accessorie = accessories.next();
					String id = accessorie.attributeValue("id");
					String module = accessorie.attributeValue("module");
					String controlpanel = accessorie.attributeValue("controlpanel");
					if(id!=null&&module!=null&&controlpanel!=null){
						String retrieve = accessorie.attributeValue("retrieve");
						String version = accessorie.attributeValue("version");
						String configuration = accessorie.attributeValue("configuration");
						HashMap<String,String> accessorieConfig = new HashMap<String,String>(4);
						accessorieConfig.put("module", module);
						accessorieConfig.put("controlpanel", controlpanel);
						if(retrieve!=null) accessorieConfig.put("retrieve", retrieve);
						if(version!=null) accessorieConfig.put("version", version);
						if(configuration!=null) accessorieConfig.put("configuration", configuration);
						this.accessories.put(id, accessorieConfig);
					}
				}
			}
			
			//parse socket
			{
				Iterator<Element> sockets = controlpanelElement.elementIterator("socket");
				while(sockets.hasNext()){
					Element socketElement = sockets.next();
					String from = socketElement.attributeValue("from");
					String to = socketElement.attributeValue("to"); 
					
					if(from!=null&&to!=null){
						ArrayList<String> argList = new ArrayList<String>();
						Iterator<Element> args = socketElement.elementIterator("arg");
						while(args.hasNext()){
							Element argElement = args.next();
							argList.add(getPropertyValue(argElement,null));
						}
						Object[] socketConfig = new Object[]{from,argList.toArray(new String[argList.size()])};
						ArrayList<Object[]> froms = this.sockets.get(to);
						if(froms == null){
							froms = new ArrayList<Object[]>();
							this.sockets.put(to, froms);
						}
						froms.add(socketConfig);
					}
				}
			}
			
		}
		private static String getPropertyValue(Element property,String defaultValue){
			Element valueElement = property.element("value");
			if(valueElement!=null){
				return valueElement.getTextTrim();
			}
			return defaultValue;
		}
	}
}
